/**
 * Copyright(c) 2014 Xunton Solutions
 *
 * History:
 *   15-10-8 上午10:40 Created by dwzhang
 */
package com.jade.bss.weixin.api.event.impl;

import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Named;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.jade.bss.weixin.WeixinException;
import com.jade.bss.weixin.api.card.event.*;
import com.jade.bss.weixin.api.event.Event;
import com.jade.bss.weixin.api.event.EventListener;
import com.jade.bss.weixin.api.event.EventManager;
import com.jade.bss.weixin.api.protocol.xml.Constants;
import com.xunton.bss.weixin.api.button.event.ButtonEvent;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 事件管理器实现。
 *
 * @author <a href="mailto:dwzhang@xunton.com">dwzhang</a> 
 * @version 1.0 15-10-8 上午10:40
 */
@Named ("bss_weixinEventManager")
public class EventManagerImpl
        implements EventManager
{
    protected Log log = LogFactory.getLog(EventManagerImpl.class);
    protected Map<String, EventListener<Event>> listenerMap;
    protected EventListener<Event> defaultListener;
    protected Map<String, Class> classMap = new HashMap<String, Class>();

    @PostConstruct
    public void init()
    {
        classMap.put("event_card_pass_check", AuditCardEvent.class);
        classMap.put("event_card_not_pass_check", AuditCardEvent.class);
        classMap.put("event_user_get_card", UserGetCardEvent.class);
        classMap.put("event_user_del_card(", UserDelCardEvent.class);
        classMap.put("event_user_consume_card", UserConsumeCardEvent.class);
        classMap.put("event_user_view_card", UserViewCardEvent.class);
        classMap.put("event_VIEW", ButtonEvent.class);
        classMap.put("event_CLICK", ButtonEvent.class);
    }

    public void setClassMap(Map<String, Class> classMap)
    {
        this.classMap = classMap;
    }

    public void setListenerMap(Map<String, EventListener<Event>> listenerMap)
    {
        this.listenerMap = listenerMap;
    }

    public void setDefaultListener(EventListener<Event> defaultListener)
    {
        this.defaultListener = defaultListener;
    }

    public String handleEvent(String eventXml)
            throws WeixinException
    {
        String result = "";
        try {
            GenericEvent genericEvent = parse(eventXml, GenericEvent.class);
            Class eventClass = getEventClass(genericEvent);
            if (eventClass == null) {
                log.warn("No class defined, discard the event:" + eventXml);
                return result;
            }

            Event event = parse(eventXml, eventClass);
            EventListener<Event> listener = getEventListener(event);
            if (listener != null) {
                result = listener.onEvent(event);
            }
            else {
                log.warn("No listener found, discard the event:" + eventXml);
            }
        }
        catch (JAXBException e) {
            throw new WeixinException(Constants.ERR_XML_FORMAT_ERROR, "parse xml error", e);
        }
        return result;

    }

    protected Class getEventClass(GenericEvent event)
    {
        String key = getKey(event);
        return classMap.get(key);
    }

    protected EventListener<Event> getEventListener(Event event)
    {
        if (listenerMap == null) {
            return defaultListener;
        }
        String key = getKey(event);
        EventListener<Event> listener = listenerMap.get(key);
        return listener == null ? defaultListener : listener;
    }

    protected String getKey(Event event)
    {
        String key = event.getMsgType();
        if (StringUtils.isNotBlank(event.getEvent())) {
            key += "_" + event.getEvent();
        }
        return key;
    }

    protected <E extends Event> E parse(String xmlStr, Class<E> classz)
            throws JAXBException
    {
        JAXBContext context = JAXBContext.newInstance(classz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        StringReader reader = new StringReader(xmlStr);
        return (E) unmarshaller.unmarshal(reader);
    }
}
